/**
    ATClab - scriptable Air Traffic Control simulations.
    Copyright (C) 2010 openatclab@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/

#include "score.h"

#include <qsound.h>
#include <qtimer.h>
#include "atc_types.h"
#include "logger.h"
#include "presenter.h"
#include <string>
#include <utility>

using namespace atc;
using std::make_pair;
using std::string;

////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
//
//--------------------------------------------------------------------------------------------------
// 
//

Score::Score( const ScoreBuilder &b ) 
		:m_initialScore(b.score)
		,m_score(0)
		,m_conflicts(0)
		,m_falseAlarms(new ConflictDeclarationMap())
		,m_currentTimeSecs(0)
        ,m_player( *(new AudioPlayer(this)) )
        ,ALREADY_ACTIONED(b.m_alreadyActionedScore)
		,HIT_CONFLICT_ONTIME(b.m_hitConflictOnTimeScore)
		,HIT_CONFLICT_NOT_ONTIME(b.m_hitConflictNotOnTimeScore)
		,HIT_AFTER_CONFLICT(b.m_hitAfterConflictScore)
		,TOO_LATE_CONFLICT(b.m_tooLateConflictScore)
		,FALSE_ALARM_CONFLICT(b.m_falseAlarmConflictScore)
		,FALSE_ALARM_CONFLICT_NON_TARGET(b.m_falseAlarmConflictNonTargetScore)
		
		,HIT_NON_CONFLICT(b.m_hitNonConflictScore)
		,TOO_LATE_NON_CONFLICT(b.m_tooLateNonConflictScore)
		,FALSE_ALARM_NON_CONFLICT(b.m_falseAlarmNonConflictScore)
		,FALSE_ALARM_NON_CONFLICT_NON_TARGET(b.m_falseAlarmNonConflictNonTargetScore)
		
		,PRIOR_CONFLICT_FOR_CONFLICT_PAIR(b.m_priorConflictForConflictPairScore)
		,PRIOR_CONFLICT_FOR_NON_CONFLICT_PAIR(b.m_priorConflictForNonConflictPairScore)
		,PRIOR_CONFLICT_FOR_NON_TARGET_PAIR(b.m_priorConflictForNonTargetPairScore)
		,PRIOR_NON_CONFLICT_FOR_CONFLICT_PAIR(b.m_priorNonConflictForConflictPairScore)
		,PRIOR_NON_CONFLICT_FOR_NON_CONFLICT_PAIR(b.m_priorNonConflictForNonConflictPairScore)
		,PRIOR_NON_CONFLICT_FOR_NON_TARGET_PAIR(b.m_priorNonConflictForNonTargetPairScore)
		
		,MISS_CONFLICT(b.m_missConflictScore)
		,MISS_NON_CONFLICT(b.m_missNonConflictScore)
		,MISSED_ACCEPTANCE( b.missed_acceptance_score )

		,AUDIO_NONE(NULL)
		,AUDIO_ALREADY_ACTIONED(m_player.register_sound_bite(b.m_alreadyActionedAudio, b.m_alreadyActionedAudioDuration))
		,AUDIO_HIT_CONFLICT_ONTIME(m_player.register_sound_bite(b.m_hitConflictOnTimeAudio, b.m_hitConflictOnTimeAudioDuration))
		,AUDIO_HIT_CONFLICT_NOT_ONTIME(m_player.register_sound_bite(b.m_hitConflictNotOnTimeAudio, b.m_hitConflictNotOnTimeAudioDuration))
		,AUDIO_HIT_AFTER_CONFLICT(m_player.register_sound_bite(b.m_hitAfterConflictAudio, b.m_hitAfterConflictAudioDuration))
		,AUDIO_TOO_LATE_CONFLICT(m_player.register_sound_bite(b.m_tooLateConflictAudio, b.m_tooLateConflictAudioDuration))
		,AUDIO_FALSE_ALARM_CONFLICT(m_player.register_sound_bite(b.m_falseAlarmConflictAudio, b.m_falseAlarmConflictAudioDuration))
		,AUDIO_FALSE_ALARM_CONFLICT_NON_TARGET(m_player.register_sound_bite(b.m_falseAlarmConflictNonTargetAudio, b.m_falseAlarmConflictNonTargetAudioDuration))
		
		,AUDIO_HIT_NON_CONFLICT(m_player.register_sound_bite(b.m_hitNonConflictAudio, b.m_hitNonConflictAudioDuration))
		,AUDIO_TOO_LATE_NON_CONFLICT(m_player.register_sound_bite(b.m_tooLateNonConflictAudio, b.m_tooLateNonConflictAudioDuration))
		,AUDIO_FALSE_ALARM_NON_CONFLICT(m_player.register_sound_bite(b.m_falseAlarmNonConflictAudio, b.m_falseAlarmNonConflictAudioDuration))
		,AUDIO_FALSE_ALARM_NON_CONFLICT_NON_TARGET(m_player.register_sound_bite(b.m_falseAlarmNonConflictNonTargetAudio, b.m_falseAlarmNonConflictNonTargetAudioDuration))
		
		,AUDIO_PRIOR_CONFLICT_FOR_CONFLICT_PAIR(m_player.register_sound_bite(b.m_priorConflictForConflictPairAudio, b.m_priorConflictForConflictPairAudioDuration))
		,AUDIO_PRIOR_CONFLICT_FOR_NON_CONFLICT_PAIR(m_player.register_sound_bite(b.m_priorConflictForNonConflictPairAudio, b.m_priorConflictForNonConflictPairAudioDuration))
		,AUDIO_PRIOR_CONFLICT_FOR_NON_TARGET_PAIR(m_player.register_sound_bite(b.m_priorConflictForNonTargetPairAudio, b.m_priorConflictForNonTargetPairAudioDuration))
		,AUDIO_PRIOR_NON_CONFLICT_FOR_CONFLICT_PAIR(m_player.register_sound_bite(b.m_priorNonConflictForConflictPairAudio, b.m_priorNonConflictForConflictPairAudioDuration))
		,AUDIO_PRIOR_NON_CONFLICT_FOR_NON_CONFLICT_PAIR(m_player.register_sound_bite(b.m_priorNonConflictForNonConflictPairAudio, b.m_priorNonConflictForNonConflictPairAudioDuration))
		,AUDIO_PRIOR_NON_CONFLICT_FOR_NON_TARGET_PAIR(m_player.register_sound_bite(b.m_priorNonConflictForNonTargetPairAudio, b.m_priorNonConflictForNonTargetPairAudioDuration))
		
		,AUDIO_MISS_CONFLICT(m_player.register_sound_bite(b.m_missConflictAudio, b.m_missConflictAudioDuration))
		,AUDIO_MISS_NON_CONFLICT( m_player.register_sound_bite(b.m_missNonConflictAudio, b.m_missNonConflictAudioDuration))
		,AUDIO_MISSED_ACCEPTANCE( m_player.register_sound_bite( b.missed_acceptance_audio, b.missed_acceptance_audio_duration ) )
		,AUDIO_BEEP( m_player.register_sound_bite(b.beep_audio, b.beep_audio_duration, true) ) 

		,ALREADY_ACTIONED_DELAY(b.m_alreadyActionedDelay)
		,HIT_CONFLICT_ONTIME_DELAY(b.m_hitConflictOnTimeDelay)
		,HIT_CONFLICT_NOT_ONTIME_DELAY(b.m_hitConflictNotOnTimeDelay)
		,HIT_AFTER_CONFLICT_DELAY(b.m_hitAfterConflictDelay)
		,TOO_LATE_CONFLICT_DELAY(b.m_tooLateConflictDelay)
		,FALSE_ALARM_CONFLICT_DELAY(b.m_falseAlarmConflictDelay)
		,FALSE_ALARM_CONFLICT_NON_TARGET_DELAY(b.m_falseAlarmConflictNonTargetDelay)
		
		,HIT_NON_CONFLICT_DELAY(b.m_hitNonConflictDelay)
		,TOO_LATE_NON_CONFLICT_DELAY(b.m_tooLateNonConflictDelay)
		,FALSE_ALARM_NON_CONFLICT_DELAY(b.m_falseAlarmNonConflictDelay)
		,FALSE_ALARM_NON_CONFLICT_NON_TARGET_DELAY(b.m_falseAlarmNonConflictNonTargetDelay)
		
		,PRIOR_CONFLICT_FOR_CONFLICT_PAIR_DELAY(b.m_priorConflictForConflictPairDelay)
		,PRIOR_CONFLICT_FOR_NON_CONFLICT_PAIR_DELAY(b.m_priorConflictForNonConflictPairDelay)
		,PRIOR_CONFLICT_FOR_NON_TARGET_PAIR_DELAY(b.m_priorConflictForNonTargetPairDelay)
		,PRIOR_NON_CONFLICT_FOR_CONFLICT_PAIR_DELAY(b.m_priorNonConflictForConflictPairDelay)
		,PRIOR_NON_CONFLICT_FOR_NON_CONFLICT_PAIR_DELAY(b.m_priorNonConflictForNonConflictPairDelay)
		,PRIOR_NON_CONFLICT_FOR_NON_TARGET_PAIR_DELAY(b.m_priorNonConflictForNonTargetPairDelay)
		
		,MISS_CONFLICT_DELAY(b.m_missConflictDelay)
		,MISS_NON_CONFLICT_DELAY(b.m_missNonConflictDelay)
		,MISSED_ACCEPTANCE_DELAY( b.m_missedAcceptanceDelay )

		,m_delayedScore(0)
		,m_conflictDeclarationScoreEnabled(true)
		,m_conflictDeclarationAudioEnabled(true)
		,m_goalSuccessScoreMultiplier(b.m_goalSuccessScoreMultiplier)
		,m_goalFailureScore(b.m_goalFailureScore)
		,m_goalSuccessFormula(b.m_goalSuccessFormula)
		,m_goalFailFormula(b.m_goalFailFormula)
		,m_eventReferenceMap(b.m_eventReferenceMap)
		,m_correctDecisionLinks(b.m_correctDecisionLinks)
		,m_incorrectDecisionLinks (b.m_incorrectDecisionLinks)
{
}

Score::~Score() 
{
}

//--------------------------------------------------------------------------------------------------
// [public slots]
//
void 
Score::zero() 
{ 
	
	// Reset the correct decision count
	for (std::map<std::string, std::string>::iterator cdlIt = m_correctDecisionLinks.begin();
			cdlIt != m_correctDecisionLinks.end(); cdlIt++)
	{
		pact::GlobalVariables::iterator it = 
			pact::Presenter::instance()->globals().find((*cdlIt).second);

		if (it != pact::Presenter::instance()->globals().end())
		{
			it->second->set_value(0);
		}
	}

	// Reset the incorrect decision count
	for (std::map<std::string, std::string>::iterator idlIt = m_incorrectDecisionLinks.begin();
			idlIt != m_incorrectDecisionLinks.end(); idlIt++)
	{
		pact::GlobalVariables::iterator it = 
			pact::Presenter::instance()->globals().find((*idlIt).second);

		if (it != pact::Presenter::instance()->globals().end())
		{
			it->second->set_value(0);
		}
	}

	update_score(m_initialScore); 
	
	emit( sig_score_quiet(m_score) ); 
}

//--------------------------------------------------------------------------------------------------
// [public slots]
//
void 
Score::DeclareConflict(	const std::string& a_ac0, 
						const std::string& a_ac1) 
{
	ConflictDeclaration *cd = conflict_declaration(a_ac0, a_ac1, atc::ConflictDeclaration::CONFLICT_TYPE_CONFLICT);

	if (cd) 
	{
		if (cd->Type() == ConflictDeclaration::CONFLICT_TYPE_CONFLICT)
		{
			// Conflict target pair

			switch (cd->Action())
			{
			case ConflictDeclaration::ACTIONED_CONFLICT:

				score(	PRIOR_CONFLICT_FOR_CONFLICT_PAIR, 
						AUDIO_PRIOR_CONFLICT_FOR_CONFLICT_PAIR, 
						EVENT_PRIOR_CONFLICT_FOR_CONFLICT_PAIR,
						PRIOR_CONFLICT_FOR_CONFLICT_PAIR_DELAY);
				break;

			case ConflictDeclaration::ACTIONED_NON_CONFLICT:
			case ConflictDeclaration::FALSE_ALARMED_CONFLICT_TARGET:
			case ConflictDeclaration::FALSE_ALARMED_CONFLICT_NON_TARGET:
			case ConflictDeclaration::FALSE_ALARMED_NON_CONFLICT_TARGET:
			case ConflictDeclaration::FALSE_ALARMED_NON_CONFLICT_NON_TARGET:

				score(	ALREADY_ACTIONED,
						AUDIO_ALREADY_ACTIONED,
						EVENT_ALREADY_ACTIONED,
						ALREADY_ACTIONED_DELAY);
				break;

			case ConflictDeclaration::ALREADY_CONFLICTED:
				score(	HIT_AFTER_CONFLICT, 
						AUDIO_HIT_AFTER_CONFLICT, 
						EVENT_HIT_AFTER_CONFLICT,
						HIT_AFTER_CONFLICT_DELAY);

				cd->SetAction(ConflictDeclaration::ACTIONED_CONFLICT);
				break;
			
			case ConflictDeclaration::NO_ACTION:
				if (cd->ConflictDeclaredOnTime(m_currentTimeSecs))
				{
					score(	HIT_CONFLICT_ONTIME, 
							AUDIO_HIT_CONFLICT_ONTIME, 
							EVENT_HIT_CONFLICT_ONTIME,
							HIT_CONFLICT_ONTIME_DELAY);

					emit sig_hit();
				}
				else
				{
					score(	HIT_CONFLICT_NOT_ONTIME, 
							AUDIO_HIT_CONFLICT_NOT_ONTIME, 
							EVENT_HIT_CONFLICT_NOT_ONTIME,
							HIT_CONFLICT_NOT_ONTIME_DELAY);
				}

                cd->SetAction(ConflictDeclaration::ACTIONED_CONFLICT);
                break;

            case ConflictDeclaration::MISSED_CONFLICT:
            case ConflictDeclaration::MISSED_NON_CONFLICT:
                break;
            }
		}
		else
		{
			// Non conflict target pair
			// False alarms
			switch (cd->Action())
			{
			case ConflictDeclaration::FALSE_ALARMED_CONFLICT_TARGET:
				score(	PRIOR_CONFLICT_FOR_NON_CONFLICT_PAIR, 
						AUDIO_PRIOR_CONFLICT_FOR_NON_CONFLICT_PAIR, 
						EVENT_PRIOR_CONFLICT_FOR_NON_CONFLICT_PAIR,
						PRIOR_CONFLICT_FOR_NON_CONFLICT_PAIR_DELAY);
				break;

			case ConflictDeclaration::ACTIONED_NON_CONFLICT:
				score (	ALREADY_ACTIONED,
						AUDIO_ALREADY_ACTIONED,
						EVENT_ALREADY_ACTIONED,
						ALREADY_ACTIONED_DELAY);
				break;

			case ConflictDeclaration::MISSED_NON_CONFLICT:
				score (	TOO_LATE_CONFLICT,
						AUDIO_TOO_LATE_CONFLICT,
						EVENT_TOO_LATE_CONFLICT,
						TOO_LATE_CONFLICT_DELAY);
				break;

			case ConflictDeclaration::NO_ACTION:
				score(	FALSE_ALARM_CONFLICT, 
						AUDIO_FALSE_ALARM_CONFLICT, 
						EVENT_FALSE_ALARM_CONFLICT,
						FALSE_ALARM_CONFLICT_DELAY);

				cd->SetAction(ConflictDeclaration::FALSE_ALARMED_CONFLICT_TARGET);
				break;

			default:
				break;
			}
		}
	}
	else 
	{
		bool found = false;
		
		if (!m_falseAlarms->empty())
		{
			ConflictDeclarationMap::iterator it;
			ConflictDeclarationMap::iterator end = m_falseAlarms->end();

			if (end != (it = m_falseAlarms->find(ConflictAircraftPair(a_ac0, a_ac1))) ||
				end != (it = m_falseAlarms->find(ConflictAircraftPair(a_ac1, a_ac0)))) 
			{
				switch (it->second.Action())
				{
				case ConflictDeclaration::FALSE_ALARMED_CONFLICT_NON_TARGET:
					score(	PRIOR_CONFLICT_FOR_NON_TARGET_PAIR, 
							AUDIO_PRIOR_CONFLICT_FOR_NON_TARGET_PAIR, 
							EVENT_PRIOR_CONFLICT_FOR_NON_TARGET_PAIR,
							PRIOR_CONFLICT_FOR_NON_TARGET_PAIR_DELAY);
					break;

				default:
					score(	ALREADY_ACTIONED,
							AUDIO_ALREADY_ACTIONED,
							EVENT_ALREADY_ACTIONED,
							ALREADY_ACTIONED_DELAY);
					break;
				}

				found = true;
			}
		}
		
		if (!found)
		{
			ConflictDeclaration c = ConflictDeclaration::CreateFalseAlarmConflict(a_ac0, a_ac1);

			FalseAlarmType faType = FalseAlarm(a_ac0, a_ac1, atc::ConflictDeclaration::CONFLICT_TYPE_CONFLICT);

			if (faType == FALSE_ALARM_NON_TARGET)
			{
				score(	FALSE_ALARM_CONFLICT_NON_TARGET, 
						AUDIO_FALSE_ALARM_CONFLICT_NON_TARGET, 
						EVENT_FALSE_ALARM_CONFLICT_NON_TARGET,
						FALSE_ALARM_CONFLICT_NON_TARGET_DELAY);
						
				c.SetAction(ConflictDeclaration::FALSE_ALARMED_CONFLICT_NON_TARGET);
			}

			m_falseAlarms->insert(std::make_pair(c.conflict_pair(), c));
		}
	}
}

void 
Score::DeclareNonConflict(	const std::string& a_ac0, 
							const std::string& a_ac1) 
{
	ConflictDeclaration *cd = conflict_declaration(a_ac0, a_ac1, atc::ConflictDeclaration::CONFLICT_TYPE_NON_CONFLICT);

	if (cd) 
	{
		if (cd->Type() == ConflictDeclaration::CONFLICT_TYPE_NON_CONFLICT)
		{
			switch (cd->Action())
			{
			case ConflictDeclaration::ACTIONED_NON_CONFLICT:

				score(	PRIOR_NON_CONFLICT_FOR_NON_CONFLICT_PAIR, 
						AUDIO_PRIOR_NON_CONFLICT_FOR_NON_CONFLICT_PAIR, 
						EVENT_PRIOR_NON_CONFLICT_FOR_NON_CONFLICT_PAIR,
						PRIOR_NON_CONFLICT_FOR_NON_CONFLICT_PAIR_DELAY);
				break;

			case ConflictDeclaration::FALSE_ALARMED_CONFLICT_TARGET:
			case ConflictDeclaration::FALSE_ALARMED_CONFLICT_NON_TARGET:
			case ConflictDeclaration::FALSE_ALARMED_NON_CONFLICT_NON_TARGET:
			case ConflictDeclaration::FALSE_ALARMED_NON_CONFLICT_TARGET:

				score(	ALREADY_ACTIONED,
						AUDIO_ALREADY_ACTIONED,
						EVENT_ALREADY_ACTIONED,
						ALREADY_ACTIONED_DELAY);
				break;

			case ConflictDeclaration::MISSED_NON_CONFLICT:
				score(	TOO_LATE_NON_CONFLICT, 
						AUDIO_TOO_LATE_NON_CONFLICT, 
						EVENT_TOO_LATE_NON_CONFLICT,
						TOO_LATE_NON_CONFLICT_DELAY);
				break;

			case ConflictDeclaration::NO_ACTION:
				score(	HIT_NON_CONFLICT, 
						AUDIO_HIT_NON_CONFLICT, 
						EVENT_HIT_NON_CONFLICT,
						HIT_NON_CONFLICT_DELAY);

				cd->SetAction(ConflictDeclaration::ACTIONED_NON_CONFLICT);
                break;

            case ConflictDeclaration::ACTIONED_CONFLICT:
            case ConflictDeclaration::MISSED_CONFLICT:
            case ConflictDeclaration::ALREADY_CONFLICTED:
                break;

			}
		}
		else
		{
			// Conflict target pairs
			// False alarms
			switch (cd->Action())
			{
			case ConflictDeclaration::FALSE_ALARMED_NON_CONFLICT_TARGET:
					score(	PRIOR_NON_CONFLICT_FOR_CONFLICT_PAIR, 
							AUDIO_PRIOR_NON_CONFLICT_FOR_CONFLICT_PAIR, 
							EVENT_PRIOR_NON_CONFLICT_FOR_CONFLICT_PAIR,
							PRIOR_NON_CONFLICT_FOR_CONFLICT_PAIR_DELAY);
					break;

			case ConflictDeclaration::ACTIONED_CONFLICT:
				score (	ALREADY_ACTIONED,
						AUDIO_ALREADY_ACTIONED,
						EVENT_ALREADY_ACTIONED,
						ALREADY_ACTIONED_DELAY);
				break;

			case ConflictDeclaration::ALREADY_CONFLICTED:
				score(	TOO_LATE_NON_CONFLICT, 
						AUDIO_TOO_LATE_NON_CONFLICT, 
						EVENT_TOO_LATE_NON_CONFLICT,
						TOO_LATE_NON_CONFLICT_DELAY);

				cd->SetAction(ConflictDeclaration::ACTIONED_NON_CONFLICT);
				break;

			case ConflictDeclaration::NO_ACTION:
			case ConflictDeclaration::MISSED_CONFLICT:

				score(	FALSE_ALARM_NON_CONFLICT, 
						AUDIO_FALSE_ALARM_NON_CONFLICT, 
						EVENT_FALSE_ALARM_NON_CONFLICT,
						FALSE_ALARM_NON_CONFLICT_DELAY);

				cd->SetAction(ConflictDeclaration::FALSE_ALARMED_NON_CONFLICT_TARGET);
				break;

			default:
				break;
			}
		}
	}
	else 
	{
		bool found = false;
		
		if (!m_falseAlarms->empty())
		{
			ConflictDeclarationMap::iterator it;
			ConflictDeclarationMap::iterator end = m_falseAlarms->end();

			if (end != (it = m_falseAlarms->find(ConflictAircraftPair(a_ac0, a_ac1))) ||
				end != (it = m_falseAlarms->find(ConflictAircraftPair(a_ac1, a_ac0)))) 
			{
				switch (it->second.Action())
				{
				case ConflictDeclaration::FALSE_ALARMED_NON_CONFLICT_NON_TARGET:
					score(	PRIOR_NON_CONFLICT_FOR_NON_TARGET_PAIR, 
							AUDIO_PRIOR_NON_CONFLICT_FOR_NON_TARGET_PAIR, 
							EVENT_PRIOR_NON_CONFLICT_FOR_NON_TARGET_PAIR,
							PRIOR_NON_CONFLICT_FOR_NON_TARGET_PAIR_DELAY);
					break;

				default:
					score(	ALREADY_ACTIONED,
							AUDIO_ALREADY_ACTIONED,
							EVENT_ALREADY_ACTIONED,
							ALREADY_ACTIONED_DELAY);
					break;
				}

				found = true;
			}
		}
		
		if (!found)
		{
			ConflictDeclaration c = ConflictDeclaration::CreateFalseAlarmNonConflict(a_ac0, a_ac1);
			FalseAlarmType faType = FalseAlarm(a_ac0, a_ac1, atc::ConflictDeclaration::CONFLICT_TYPE_NON_CONFLICT);

			if (faType == FALSE_ALARM_NON_TARGET) 
			{
				score(	FALSE_ALARM_NON_CONFLICT_NON_TARGET, 
						AUDIO_FALSE_ALARM_NON_CONFLICT_NON_TARGET, 
						EVENT_FALSE_ALARM_NON_CONFLICT_NON_TARGET,
						FALSE_ALARM_NON_CONFLICT_NON_TARGET_DELAY);
				
				c.SetAction(ConflictDeclaration::FALSE_ALARMED_NON_CONFLICT_NON_TARGET);
			}	

			m_falseAlarms->insert(std::make_pair(c.conflict_pair(), c));
		}
	}
}

void 
Score::conflict(const std::string& a_ac0, 
				const std::string& a_ac1) 
{
	ConflictDeclaration *cd = conflict_declaration(a_ac0, a_ac1, atc::ConflictDeclaration::CONFLICT_TYPE_CONFLICT);
	
	if (cd) 
	{
		// Only penalise for a conflict if a participant has not responded at all
        if ((cd->Action() != ConflictDeclaration::ACTIONED_CONFLICT) &&
            (cd->Action() != ConflictDeclaration::ACTIONED_NON_CONFLICT) &&
            (cd->Action() != ConflictDeclaration::FALSE_ALARMED_NON_CONFLICT_TARGET))
		{
			score(	TOO_LATE_CONFLICT, 
					AUDIO_TOO_LATE_CONFLICT, 
					EVENT_TOO_LATE_CONFLICT,
					TOO_LATE_CONFLICT_DELAY);

			cd->SetAction(ConflictDeclaration::ALREADY_CONFLICTED);
		}
	}
}

void
Score::CheckMissedNonConflicts(bool a_compareToCurrentTime, bool a_playAudio)
{
	for (ConflictDeclarationMap::iterator it = m_conflicts->begin(); 
			it != m_conflicts->end(); it++)
	{
		if ((it->second.Type() == ConflictDeclaration::CONFLICT_TYPE_NON_CONFLICT) &&
			(it->second.Action() == ConflictDeclaration::NO_ACTION))
		{
			bool missed = true;

			if (a_compareToCurrentTime)
			{
				missed = (m_currentTimeSecs >= it->second.FinalTime());
			}

			if (missed)
			{
				SoundBite audio = AUDIO_NONE;

				if (a_playAudio)
				{
					audio = AUDIO_MISS_NON_CONFLICT;
				}

				score(	MISS_NON_CONFLICT, 
						audio, 
						EVENT_MISS_NON_CONFLICT,
						MISS_NON_CONFLICT_DELAY);

				it->second.SetAction(ConflictDeclaration::MISSED_NON_CONFLICT);
			}
		}
	}
}

void
Score::CheckMissedConflicts(bool a_compareToCurrentTime, bool a_playAudio)
{
	for (ConflictDeclarationMap::iterator it = m_conflicts->begin(); 
			it != m_conflicts->end(); it++)
	{
		if ((it->second.Type() == ConflictDeclaration::CONFLICT_TYPE_CONFLICT) &&
			(it->second.Action() == ConflictDeclaration::NO_ACTION))
		{
			bool missed = true;

			if (a_compareToCurrentTime)
			{
				missed = (m_currentTimeSecs >= it->second.FinalTime());
			}

			if (missed)
			{
				SoundBite audio = AUDIO_NONE;

				if (a_playAudio)
				{
                    audio = AUDIO_MISS_CONFLICT;
				}

				score(	MISS_CONFLICT, 
						audio, 
						EVENT_MISS_CONFLICT,
						MISS_CONFLICT_DELAY);

				it->second.SetAction(ConflictDeclaration::MISSED_CONFLICT);
			}
		}
	}
}

void Score::missed_acceptance() {
	score(	MISSED_ACCEPTANCE, 
			AUDIO_MISSED_ACCEPTANCE, 
			EVENT_MISSED_ACCEPTANCE,
			MISSED_ACCEPTANCE_DELAY);
}

void 
Score::UpdateTime(long a_seconds)
{
	m_currentTimeSecs = a_seconds;
	
	CheckMissedNonConflicts();
}

void
Score::UpdateGoalScores()
{
	int retVal1 = 0;
	int retVal2 = 0;
	bool achievedGoal = false;
	logging::KVPairs kvs;

	pact::GlobalVariables::iterator it1 = 
			pact::Presenter::instance()->globals().find(m_goalSuccessFormula.m_ref1);

	if (it1 != pact::Presenter::instance()->globals().end())
	{
		retVal1 = it1->second->value();
	}

	pact::GlobalVariables::iterator it2 = 
			pact::Presenter::instance()->globals().find(m_goalSuccessFormula.m_ref2);

	if (it2 != pact::Presenter::instance()->globals().end())
	{
		retVal2 = it2->second->value();
	}

	if (m_goalSuccessFormula.m_comparisonOperator == "lessThan")
	{
		achievedGoal = (retVal1 < retVal2);
	}
	else if (m_goalSuccessFormula.m_comparisonOperator == "lessThanOrEqualTo")
	{
		achievedGoal = (retVal1 <= retVal2);
	}
	else if (m_goalSuccessFormula.m_comparisonOperator == "greaterThan")
	{
		achievedGoal = (retVal1 > retVal2);
	}
	else if (m_goalSuccessFormula.m_comparisonOperator == "greaterThanOrEqualTo")
	{
		achievedGoal = (retVal1 >= retVal2);
	}

	int score = 0;
	if (achievedGoal)
	{
		score = retVal2 * m_goalSuccessScoreMultiplier;
	}
	else
	{
		score = m_goalFailureScore;
	}

	kvs.push_back(make_pair(string("correct_decisions"), atc::to_string(retVal1)));
	kvs.push_back(make_pair(string("goal"), atc::to_string(retVal2)));
	kvs.push_back(make_pair(string("success"), (achievedGoal?"true":"false")));
	kvs.push_back(make_pair(string("goal_score"), atc::to_string(score)));
	kvs.push_back(make_pair(string("total_score"), atc::to_string(m_score)));
	logging::Logger::instance()->info( kvs );

	m_score += score;
}

//--------------------------------------------------------------------------------------------------
// [private]
//
ConflictDeclaration* 
Score::conflict_declaration(const std::string&	a_ac0, 
							const std::string&	a_ac1,
							ConflictDeclaration::ConflictType		a_type) 
{
	ConflictDeclaration *retVal = 0;

	std::string type;

	switch (a_type)
	{
	case ConflictDeclaration::CONFLICT_TYPE_CONFLICT:
		type = "conflict_declaration";
		break;
	case ConflictDeclaration::CONFLICT_TYPE_NON_CONFLICT:
		type = "non_conflict_declaration";
		break;
	default:
		type = "unknown_declaration";
		break;
	}

	logging::KVPairs kvs;
	kvs.push_back(make_pair(string("score"), type));
	kvs.push_back(make_pair(string("aircraft0"), a_ac0));
	kvs.push_back(make_pair(string("aircraft1"), a_ac1));

	ConflictDeclarationMap::iterator it;
	ConflictDeclarationMap::iterator end = m_conflicts->end();

	if (end != (it = m_conflicts->find(ConflictAircraftPair(a_ac0, a_ac1))) ||
		end != (it = m_conflicts->find(ConflictAircraftPair(a_ac1, a_ac0)))) 
	{
		retVal = &(it->second);

		kvs.push_back(make_pair( string("seperation"), to_string(retVal->seperation())) );

		if (a_type == ConflictDeclaration::CONFLICT_TYPE_CONFLICT)
		{
			kvs.push_back(make_pair(string("actioned"), to_string(retVal->Action() == ConflictDeclaration::ACTIONED_CONFLICT)));
			kvs.push_back(make_pair(string("missed"), to_string(retVal->Action() == ConflictDeclaration::MISSED_CONFLICT)));
		}
		else
		{
			kvs.push_back(make_pair(string("actioned"), to_string(retVal->Action() == ConflictDeclaration::ACTIONED_NON_CONFLICT)));
			kvs.push_back(make_pair(string("missed"), to_string(retVal->Action() == ConflictDeclaration::MISSED_NON_CONFLICT)));
		}

		if (it->second.Type() == a_type)
		{
			kvs.push_back(make_pair(string("false_alarm"), to_string(false)));
		}
		else
		{
			kvs.push_back(make_pair(string("false_alarm"), to_string(true)));
		}
	}
	logging::Logger::instance()->info( kvs );

	return retVal;
}

FalseAlarmType	
Score::FalseAlarm(	std::string a_ac0, 
					std::string a_ac1, 
					ConflictDeclaration::ConflictType a_type) const
{
	FalseAlarmType ret = NO_FALSE_ALARM;

	ConflictDeclarationMap::iterator it;
	ConflictDeclarationMap::iterator end = m_conflicts->end();

	if (end != (it = m_conflicts->find(ConflictAircraftPair(a_ac0, a_ac1))) ||
		end != (it = m_conflicts->find(ConflictAircraftPair(a_ac1, a_ac0)))) 
	{
		if (it->second.Type() != a_type)
		{
			ret = FALSE_ALARM_TARGET;
		}
	}
	else
	{
		ret = FALSE_ALARM_NON_TARGET;
	}

	return ret;
}

void 
Score::score(	int					a_score, 
				SoundBite			a_soundbite, 
				const std::string&	a_event,
				int					a_delay) 
{
	logging::KVPairs kvs;
	std::string globalVariable;

	// Update the event tally count in its corresponding global variable
	std::map<std::string, std::string>::iterator refMapIt = m_eventReferenceMap.find(a_event);
	if (refMapIt != m_eventReferenceMap.end())
	{
		int count = 0;

		globalVariable = refMapIt->second;

		pact::GlobalVariables::iterator it = 
			pact::Presenter::instance()->globals().find(globalVariable);

		if (it != pact::Presenter::instance()->globals().end())
		{
			count = it->second->value();
			it->second->set_value(++count);
		}
	}

	// Update the correct decision count if applicable
	std::map<std::string, std::string>::iterator cdlIt = m_correctDecisionLinks.find(globalVariable);

	if (cdlIt != m_correctDecisionLinks.end())
	{
		int count = 0;

		pact::GlobalVariables::iterator it = 
			pact::Presenter::instance()->globals().find((*cdlIt).second);

		if (it != pact::Presenter::instance()->globals().end())
		{
			count = it->second->value();
			it->second->set_value(++count);
		}
	}

	// Update the incorrect decision count if applicable
	std::map<std::string, std::string>::iterator idlIt = m_incorrectDecisionLinks.find(globalVariable);

	if (idlIt != m_incorrectDecisionLinks.end())
	{
		int count = 0;

		pact::GlobalVariables::iterator it = 
			pact::Presenter::instance()->globals().find((*idlIt).second);

		if (it != pact::Presenter::instance()->globals().end())
		{
			count = it->second->value();
			it->second->set_value(++count);
		}
	}

	if (m_conflictDeclarationScoreEnabled)
	{
		m_score += a_score;
		m_delayedScore = a_score;

		kvs.push_back(make_pair(string("score"),		to_string(a_score)));
		kvs.push_back(make_pair(string("total_score"),	to_string(m_score)));
	}

	if (m_conflictDeclarationAudioEnabled)
	{
		if (a_soundbite != AUDIO_NONE)
		{
			kvs.push_back(make_pair(string("sound"), a_soundbite->sound.fileName()));
			m_delayedSoundbite = a_soundbite;
		}
	}

	if (m_conflictDeclarationScoreEnabled || m_conflictDeclarationAudioEnabled)
	{
		kvs.push_back(make_pair(string("event"),		a_event));
		kvs.push_back(make_pair(string("elapsed"),		to_string(m_currentTimeSecs)));
		logging::Logger::instance()->info( kvs );
	
		if (a_soundbite != AUDIO_NONE)
		{
            //QTimer *timer = new QTimer(this);
			QTimer::singleShot(a_delay * 1000, this, SLOT(ProvideFeedback()));
		}
	}
}

void
Score::ProvideFeedback()
{
	if (!m_delayedScore) 
	{
		if (m_conflictDeclarationAudioEnabled)
		{
			m_player.queue(m_delayedSoundbite);
		}
	}
	else 
	{
		if (m_conflictDeclarationAudioEnabled)
		{
			m_player.queue(AUDIO_BEEP);
			m_player.queue(m_delayedSoundbite);
		}

		if (m_conflictDeclarationScoreEnabled)
		{
			update_score(m_score);
		}
	}
}

void 
Score::update_score(long s) 
{
	m_score = s;

	pact::Presenter *p = pact::Presenter::instance();
	pact::GlobalVariables vars = p->globals();
	pact::GlobalVariables::iterator vit = vars.find( "score" );
	if ( vit != vars.end() )
			vit->second->set_value(m_score);

	emit(sig_score(m_score));
}


////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
//
//--------------------------------------------------------------------------------------------------
// 
//
ScoreBuilder::ScoreBuilder() 
		:score(0)
		,m_alreadyActionedScore(0)
		,m_hitConflictOnTimeScore(0)
		,m_hitConflictNotOnTimeScore(0)
		,m_hitAfterConflictScore(0)
		,m_tooLateConflictScore(0)
		,m_falseAlarmConflictScore(0)
		,m_falseAlarmConflictNonTargetScore(0)

		,m_hitNonConflictScore(0)
		,m_tooLateNonConflictScore(0)
		,m_falseAlarmNonConflictScore(0)
		,m_falseAlarmNonConflictNonTargetScore(0)
		
		,m_priorConflictForConflictPairScore(0)
		,m_priorConflictForNonConflictPairScore(0)
		,m_priorConflictForNonTargetPairScore(0)
		,m_priorNonConflictForConflictPairScore(0)
		,m_priorNonConflictForNonConflictPairScore(0)
		,m_priorNonConflictForNonTargetPairScore(0)
		
		,m_missConflictScore(0)
		,m_missNonConflictScore(0)		
		,missed_acceptance_score(0)

		,m_alreadyActionedAudio()
		,m_hitConflictOnTimeAudio()
		,m_hitConflictNotOnTimeAudio()
		,m_hitAfterConflictAudio()
		,m_tooLateConflictAudio()
		,m_falseAlarmConflictAudio()
		,m_falseAlarmConflictNonTargetAudio()

		,m_hitNonConflictAudio()
		,m_tooLateNonConflictAudio()
		,m_falseAlarmNonConflictAudio()
		,m_falseAlarmNonConflictNonTargetAudio()

		,m_priorConflictForConflictPairAudio()
		,m_priorConflictForNonConflictPairAudio()
		,m_priorConflictForNonTargetPairAudio()
		,m_priorNonConflictForConflictPairAudio()
		,m_priorNonConflictForNonConflictPairAudio()
		,m_priorNonConflictForNonTargetPairAudio()

		,m_missConflictAudio()
		,m_missNonConflictAudio()
		,missed_acceptance_audio()

		,m_alreadyActionedAudioDuration(0)
		,m_hitConflictOnTimeAudioDuration(0)
		,m_hitConflictNotOnTimeAudioDuration(0)
		,m_hitAfterConflictAudioDuration(0)
		,m_tooLateConflictAudioDuration(0)
		,m_falseAlarmConflictAudioDuration(0)
		,m_falseAlarmConflictNonTargetAudioDuration(0)

		,m_hitNonConflictAudioDuration(0)
		,m_tooLateNonConflictAudioDuration(0)
		,m_falseAlarmNonConflictAudioDuration(0)
		,m_falseAlarmNonConflictNonTargetAudioDuration(0)

		,m_priorConflictForConflictPairAudioDuration(0)
		,m_priorConflictForNonConflictPairAudioDuration(0)
		,m_priorConflictForNonTargetPairAudioDuration(0)
		,m_priorNonConflictForConflictPairAudioDuration(0)
		,m_priorNonConflictForNonConflictPairAudioDuration(0)
		,m_priorNonConflictForNonTargetPairAudioDuration(0)
		
		,m_missConflictAudioDuration(0)
		,m_missNonConflictAudioDuration(0)
		,missed_acceptance_audio_duration(0)

		,m_alreadyActionedDelay(0)
		,m_hitConflictOnTimeDelay(0)
		,m_hitConflictNotOnTimeDelay(0)
		,m_hitAfterConflictDelay(0)
		,m_tooLateConflictDelay(0)
		,m_falseAlarmConflictDelay(0)
		,m_falseAlarmConflictNonTargetDelay(0)
		,m_hitNonConflictDelay(0)
		,m_tooLateNonConflictDelay(0)
		,m_falseAlarmNonConflictDelay(0)
		,m_falseAlarmNonConflictNonTargetDelay(0)
		,m_priorConflictForConflictPairDelay(0)
		,m_priorConflictForNonConflictPairDelay(0)
		,m_priorConflictForNonTargetPairDelay(0)
		,m_priorNonConflictForConflictPairDelay(0)
		,m_priorNonConflictForNonConflictPairDelay(0)
		,m_priorNonConflictForNonTargetPairDelay(0)
		,m_missConflictDelay(0)
		,m_missNonConflictDelay(0)
		,m_missedAcceptanceDelay(0)
		,beep_audio( "beep.wav" )
		,beep_audio_duration( 1200 )
		,m_goalSuccessScoreMultiplier(0)
		,m_goalFailureScore(0)
		,m_goalSuccessFormula()
		,m_goalFailFormula()
		,m_eventReferenceMap()
		,m_correctDecisionLinks()
		,m_incorrectDecisionLinks()
{
}

ScoreBuilder::~ScoreBuilder() 
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
